# 导入工具包
from imutils import contours
import numpy as np
import argparse
import cv2
import myutils
import os
from num_template import get_digits


# 对银行卡图片进行预处理
#     进行形态学上的一些操作： 去除干扰项（背景，不相关区域）
# 1. 初始化卷积核： 获取核，是一个矩形，在滤波或形态学算法时使用
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))

# 绘图展示
def cv_show(name,img):
    pass
	#cv2.imshow(name, img)
	#cv2.waitKey(0)
	#cv2.destroyAllWindows()
dir = os.path.dirname(os.path.abspath(__file__))

template_path = os.path.join(dir, "images/ocr_a_reference.png")
 
digits = get_digits(template_path)

def get_value_from_contour(group, contour, digitColl):
    # 找到当前数值的轮廓，resize成合适的的大小
    (x, y, w, h) = cv2.boundingRect(contour)
    roi = group[y:y + h, x:x + w]
    # 57,88来自获取模板时的设置，详见num_template.py
    roi = cv2.resize(roi, (57, 88))
    cv_show('roi',roi)

    # 计算匹配得分
    scores = []

    # 在模板中计算每一个得分
    for (digit, digitROI) in digitColl.items():
        # 模板匹配: 获取模板得分
        result = cv2.matchTemplate(roi, digitROI,
            cv2.TM_CCOEFF)
        # 获取最大得分
        (_, score, _, _) = cv2.minMaxLoc(result)
        scores.append(score)
    return np.argmax(scores)
output = []
def getContours(gray):
    cv_show('gray',gray)
    # 4. 礼帽操作，突出更明亮的区域： 突出字体的区域（更明亮的区域），过滤掉其他区域
    tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel) 
    cv_show('tophat',tophat) 
    # 5. 计算图像梯度： 获取Sobel算子
    #  参数2： 图像深度：
    #  参数3，4： 算水平还是垂直方向
    #  参数4： 计算的核
    gradX = cv2.Sobel(tophat, ddepth=cv2.CV_64F, dx=1, dy=0, #ksize=-1相当于用3*3的
        ksize=-1)
    # 6. 边缘检测
    gradX = np.absolute(gradX)
    (minVal, maxVal) = (np.min(gradX), np.max(gradX))
    gradX = (255 * ((gradX - minVal) / (maxVal - minVal)))
    gradX = gradX.astype("uint8")

    #print (np.array(gradX).shape)
    cv_show('gradX',gradX)

    # # 7. 通过闭操作（先膨胀，再腐蚀）将数字连在一起
    gradX = cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, rectKernel) 
    cv_show('gradX',gradX)
    # 8. 二值化处理-进行阈阀计算：THRESH_OTSU会自动寻找合适的阈值，适合双峰，需把阈值参数设置为0
    #   将两种主题分开， 不清楚阈值什么值比较合适，希望进行自动判断，所以传入0,255
    thresh = cv2.threshold(gradX, 0, 255,
        cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1] 
    cv_show('thresh',thresh)

    # 9. 有些区域内部有空格，希望填充起来：再来一个闭操作
    thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel) #再来一个闭操作
    cv_show('thresh',thresh)
    # 10. 计算轮廓
    threshCnts, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
        cv2.CHAIN_APPROX_SIMPLE)
    return threshCnts, hierarchy
def filter_contour(threshCnts, hierarchy, image):
    cnts = threshCnts
    cur_img = image.copy()
    # 11. 在resize图片中画出轮廓
    cv2.drawContours(cur_img,cnts,-1,(0,0,255),3) 
    cv_show('img',cur_img)
    locs = []

    # 11. 轮廓过滤：遍历轮廓
    for (i, c) in enumerate(cnts):
        # 计算矩形， 获取轮廓特征，例如：长宽比
        # 11.1 做出外接矩形
        (x, y, w, h) = cv2.boundingRect(c)
        # 11.2 计算长宽比
        ar = w / float(h)

        # 11.3 选择合适的区域，根据实际任务来，这里的基本都是四个数字一组
        if ar > 2.5 and ar < 4.0:

            if (w > 40 and w < 55) and (h > 10 and h < 20):
                #符合的留下来
                locs.append((x, y, w, h))
    return locs
def get_number_contours(locs, gray, image):
    output = []
    # 12. 将符合的轮廓从左到右排序
    locs = sorted(locs, key=lambda x:x[0])
    print(len(locs))    
    # 13. 遍历每一个轮廓中的数字
    for (i, (gX, gY, gW, gH)) in enumerate(locs):
        # initialize the list of group digits
        groupOutput = []

        # 13.1 根据坐标提取每一个组： 轮廓稍微扩大些
        group = gray[gY - 5:gY + gH + 5, gX - 5:gX + gW + 5]
        cv_show('group',group)
        # 13.2 预处理： 进行二值化处理（使用大金算法）
        group = cv2.threshold(group, 0, 255,
            cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
        cv_show('group',group)
        # 13.3 计算每一组的轮廓
        digitCnts,hierarchy = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL,
            cv2.CHAIN_APPROX_SIMPLE)
        # 13.4 对轮廓进行排序
        digitCnts = contours.sort_contours(digitCnts,
            method="left-to-right")[0]
        # 13.5 计算每一组中的每一个数值
        for c in digitCnts:
            val = get_value_from_contour(group, c, digits)
            # 得到最合适的数字
            groupOutput.append(str(val))

        # 画出来
        cv2.rectangle(image, (gX - 5, gY - 5),
            (gX + gW + 5, gY + gH + 5), (0, 0, 255), 1)
        cv2.putText(image, "".join(groupOutput), (gX, gY - 15),
            cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)

        # 得到结果
        output.extend(groupOutput)
    return output
outputs = []
def read_img(path):
    # 1. 读取输入图像，预处理
    image = cv2.imread(path)
    cv_show('image',image)
    
    # 2. 重新设置大小
    image = myutils.resize(image, width=300)
    
    # 3. 生成灰度图
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    return image, gray
def on_card_picture(path):
    image, gray = read_img(path)
    threshCnts, hierarchy = getContours(gray)
    locs = filter_contour(threshCnts, hierarchy, image)
    outputs = get_number_contours(locs, gray, image)
    return image, outputs

# 指定信用卡类型
FIRST_NUMBER = {
	"3": "American Express",
	"4": "Visa",
	"5": "MasterCard",
	"6": "Discover Card"
}

def on_card(path):
    card_picture_path = os.path.join(dir, path)
    image, outputs =  on_card_picture(card_picture_path)
    card_type=FIRST_NUMBER[outputs[0]]
    print(f"Credit Card Type: {card_type}")
    print("Credit Card #: {}".format("".join(outputs)))
    cv2.imshow("Image", image)
    cv2.waitKey()

on_card("images/credit_card_01.png")
on_card("images/credit_card_05.png")